package com.gp.ssmplus.base.core.mybatis.query;

import cn.hutool.core.util.ClassUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.gp.ssmplus.base.core.mybatis.base.vo.JoinBean;
import com.gp.ssmplus.base.core.mybatis.common.TableAlias;
import com.gp.ssmplus.base.core.mybatis.common.utils.*;
import com.gp.ssmplus.base.core.mybatis.entity.EntityMap;
import com.gp.ssmplus.base.core.mybatis.model.GetModel;
import com.gp.ssmplus.base.core.mybatis.model.ListModel;
import com.gp.ssmplus.base.core.mybatis.model.PageModel;
import com.gp.ssmplus.base.core.mybatis.model.PageParams;
import org.springframework.core.annotation.AnnotationUtils;

import java.util.*;

/**
 * @author: zyf
 * @date: 2018/9/4 8:42
 * @@desc: 自定义查询构造器
 */
public class CriteriaQuery<T> extends QueryWrapper<T> {

    /**
     * 外键表别名对象
     */
    protected Map<String, String> aliasMap = new HashMap<>();

    protected List<JoinBean> joinSqList = new ArrayList<>();

    /**
     * 查询字段
     */
    protected HashSet<String> select = new HashSet<>();
    /**
     * 分页对象
     */
    private PageParams pageParams;
    /**
     * 请求参数对象
     */
    private EntityMap requestMap = new EntityMap();
    /**
     * 扩展事件名称
     */
    private String handlerName;
    /**
     * 查询主表实体类Class
     */
    private Class<T> cls;

    private T model;
    /**
     * 表名
     */
    private String tableName;

    /**
     * 表别名
     */
    private String tableAlias;

    /**
     * 主键字段
     */
    private String idField;


    private PageModel pageModel;
    private ListModel listModel;


    public CriteriaQuery(PageParams pageParams) {
        this.pageParams = pageParams;
        pageParams.setCurrent(pageParams.getPage());
        pageParams.setSize(pageParams.getLimit());
        String order = pageParams.getOrder();
        apply("1=1");
        if (FlymeUtils.isNotEmpty(order)) {
            //自动添加ordery by
            String sort = pageParams.getSort();
            boolean isAsc = StringUtils.equalsIgnoreCase(SqlKeyword.ASC.name(), sort);
            orderBy(true, isAsc, order);
        }
    }

    public CriteriaQuery(Map map, Class<T> cls) {
        this.cls = cls;
        this.pageParams = new PageParams(map);
        this.tableName = ReflectionUtils.getTableName(cls);
        this.tableAlias = ReflectionUtils.getAlias(cls);
        this.idField = ReflectionUtils.getTableField(cls);
        String handlerName = pageParams.getHandlerName();
        pageParams.setCurrent(pageParams.getPage());
        pageParams.setSize(pageParams.getLimit());
        String order = FlymeUtils.getString(pageParams.getOrder(),"");
        T t = pageParams.mapToObject(cls);
        EntityMap test = new EntityMap();
        Object params = map.get("params");
        if (FlymeUtils.isNotEmpty(params)) {
            //TODO 便于接口文档测试
            test = JsonUtils.jsonToBean(params.toString(), EntityMap.class);
            handlerName = Optional.ofNullable(handlerName).orElse(test.get("handlerName"));
            t = Optional.ofNullable(t).orElse(JsonUtils.jsonToBean(params.toString(), cls));
        }
        this.setHandlerName(handlerName);
        this.setRequestMap(pageParams, test);
        this.setModel(t);
        //apply("1=1");
        //如果有排序设置排序字段
        if (FlymeUtils.isNotEmpty(order)) {
            //添加ordery by
            String sort = FlymeUtils.getString(Optional.ofNullable(pageParams.getSort()).orElse(requestMap.get("sort")), SqlKeyword.DESC.name());
            this.orderBy(order, sort);
        }
        Long idValue = Optional.ofNullable(requestMap.getLong(idField)).orElse(FlymeUtils.getLong(requestMap.get("id")));
        if (FlymeUtils.isNotEmpty(idValue)) {
            this.eq(true, tableAlias + "." + idField, idValue);
        }
    }


    public CriteriaQuery(GetModel model, Class<T> cls) {
        this.cls = cls;
        this.tableName = ReflectionUtils.getTableName(cls);
        this.tableAlias = ReflectionUtils.getAlias(cls);
        this.idField = ReflectionUtils.getTableField(cls);
        Long id = model.getId();
        String handlerName = model.getHandlerName();
        this.setHandlerName(handlerName);
        String params = model.getParams();
        if (FlymeUtils.isNotEmpty(params)) {
            this.setRequestMap(JsonUtils.jsonToBean(params, EntityMap.class));

        }
        Long idValue = Optional.ofNullable(model.getId()).orElse(this.requestMap.getLong(idField));
        if (FlymeUtils.isNotEmpty(idValue)) {
            this.eq(true, tableAlias + "." + idField, idValue);
        }
    }

    public CriteriaQuery(PageModel pageModel, Class<T> cls) {
        this.cls = cls;
        this.tableName = ReflectionUtils.getTableName(cls);
        this.tableAlias = ReflectionUtils.getAlias(cls);
        this.pageModel = pageModel;
        this.pageParams = new PageParams(pageModel);
        this.idField = ReflectionUtils.getTableField(cls);
        String handlerName = pageModel.getHandlerName();
        this.setHandlerName(handlerName);
        String params = pageModel.getParams();
        if (FlymeUtils.isNotEmpty(params)) {
            this.setRequestMap(JsonUtils.jsonToBean(params, EntityMap.class));
            this.setModel(JsonUtils.jsonToBean(params, cls));
        }
        String order = FlymeUtils.getString(pageParams.getOrder(), "");
        apply("1=1");
        if (FlymeUtils.isNotEmpty(order)) {
            //添加ordery by
            String sort = FlymeUtils.getString(pageParams.getSort(), SqlKeyword.DESC.name());
            boolean isAsc = StringUtils.equalsIgnoreCase(SqlKeyword.ASC.name(), sort);
            orderBy(true, isAsc, tableAlias + "." + order);
        }
    }


    public CriteriaQuery(Class<T> cls) {
        this.cls = cls;
        this.tableName = ReflectionUtils.getTableName(cls);
        this.tableAlias = ReflectionUtils.getAlias(cls);
        this.idField = ReflectionUtils.getTableField(cls);
    }

    public CriteriaQuery(ListModel model, Class<T> cls) {
        this.cls = cls;
        this.tableName = ReflectionUtils.getTableName(cls);
        this.tableAlias = ReflectionUtils.getAlias(cls);
        this.idField = ReflectionUtils.getTableField(cls);
        this.listModel = model;
        String handlerName = model.getHandlerName();
        this.setHandlerName(handlerName);
        String params = model.getParams();
        if (FlymeUtils.isNotEmpty(params)) {
            this.setRequestMap(JsonUtils.jsonToBean(params, EntityMap.class));
        }
    }

    /**
     * 设置targetEntity
     */
    public CriteriaQuery<T> setTargetEntity(Class cls) {
        TableAlias tableAlias = AnnotationUtils.getAnnotation(cls, TableAlias.class);
        super.eq(true, Objects.requireNonNull(tableAlias).value() + ".targetEntity", cls.getSimpleName());
        return this;
    }


    /**
     * 创建外键表关联对象,需要在mapper(xml)中编写join
     */
    @Deprecated
    public void createAlias(String entiry, String alias) {
        this.aliasMap.put(entiry, alias);
    }


    /**
     * 创建外键表关联对象,需要在mapper(xml)中编写join
     */
    @Deprecated
    public void createAlias(Class right) {
        TableAlias tableAlias = AnnotationUtils.getAnnotation(right, TableAlias.class);
        if (FlymeUtils.isNotEmpty(tableAlias)) {
            this.aliasMap.put(tableAlias.value(), tableAlias.value());
        }
    }

    /**
     * 创建连接查询
     *
     * @param joinCls 要连接查询的表对象
     */
    public JoinBean createJoin(Class joinCls) {
        ApiAssert.isNotEmpty("请调用CriteriaQuery(PageModel pageModel, Class cls)方法", cls);
        JoinBean joinBean = new JoinBean(cls, joinCls);
        this.joinSqList.add(joinBean);
        return joinBean;
    }

    /**
     * 创建连接查询
     *
     * @param strCls 要连接查询的表对象
     */
    public JoinBean createJoin(String strCls) {
        Class joinCls = ClassUtil.loadClass(strCls);
        ApiAssert.isNotEmpty("请调用CriteriaQuery(PageModel pageModel, Class cls)方法", joinCls);
        JoinBean joinBean = new JoinBean(cls, joinCls);
        this.joinSqList.add(joinBean);
        return joinBean;
    }


    /**
     * 等于
     */
    @Override
    public CriteriaQuery<T> eq(String column, Object val) {
        super.eq(FlymeUtils.isNotEmpty(val) && !val.equals(-1) && !val.equals(-1L), column, val);
        return this;
    }


    /**
     * 等于
     */
    public CriteriaQuery<T> eq(String column) {
        Object val = requestMap.get(column);
        super.eq(FlymeUtils.isNotEmpty(val) && !val.equals(-1) && !val.equals(-1L), column, val);
        return this;
    }


    /**
     * eq条件且字段会作为查询条件
     */
    public CriteriaQuery<T> eq(Class cls, String field) {
        TableAlias tableAlias = AnnotationUtils.getAnnotation(cls, TableAlias.class);
        String alias = tableAlias.value();
        Object v = requestMap.get(field);
        super.eq(FlymeUtils.isNotEmpty(v), alias + "." + field, requestMap.get(field));
        return this;
    }


    /**
     * eq条件且字段会作为查询条件
     */
    public CriteriaQuery<T> eq(Class cls, String field, Object v) {
        TableAlias tableAlias = AnnotationUtils.getAnnotation(cls, TableAlias.class);
        String alias = tableAlias.value();
        super.eq(true, alias + "." + field, v);
        return this;
    }


    /**
     * like
     */
    @Override
    public CriteriaQuery<T> like(String column, Object val) {
        super.like(FlymeUtils.isNotEmpty(val), column, val);
        return this;
    }

    public CriteriaQuery<T> orderBy(String order, String sort) {
        if (FlymeUtils.isNotEmpty(order)) {
            Boolean isAsc = StringUtils.equalsIgnoreCase(SqlKeyword.ASC.name(), sort);
            super.orderBy(true, isAsc, order);
        }
        return this;
    }

    /**
     * like并设置查询字段
     */
    public CriteriaQuery<T> likeByField(Class cls, String field) {
        TableAlias tableAlias = AnnotationUtils.getAnnotation(cls, TableAlias.class);
        String alias = tableAlias.value();
        Object v = requestMap.get(field);
        super.like(FlymeUtils.isNotEmpty(v), alias + "." + field, v);
        return this;
    }

    /**
     * in
     */
    @Override
    public CriteriaQuery<T> in(String column, Object... val) {
        super.in(FlymeUtils.isNotEmpty(val) && FlymeUtils.isNotEmpty(val[0]), column, val);
        return this;
    }


    /**
     * ge
     */
    @Override
    public CriteriaQuery<T> ge(String column, Object val) {
        super.ge(FlymeUtils.isNotEmpty(val), column, val);
        return this;
    }

    /**
     * le
     */
    @Override
    public CriteriaQuery<T> le(String column, Object val) {
        super.le(FlymeUtils.isNotEmpty(val), column, val);
        return this;
    }

    /**
     * lt
     */
    @Override
    public CriteriaQuery<T> lt(String column, Object val) {
        super.lt(FlymeUtils.isNotEmpty(val), column, val);
        return this;
    }

    /**
     * gt
     */
    @Override
    public CriteriaQuery<T> gt(String column, Object val) {
        super.gt(FlymeUtils.isNotEmpty(val), column, val);
        return this;
    }

    /**
     * limit
     */
    public CriteriaQuery<T> limit(Integer num) {
        if (num > 0) {
            super.last("limit " + num);
        }
        return this;
    }


    /**
     * or
     */
    @Override
    public CriteriaQuery<T> or() {
        super.or();
        return this;
    }

    /**
     * likeLeft
     */
    @Override
    public QueryWrapper<T> likeLeft(String column, Object val) {
        return super.likeLeft(FlymeUtils.isNotEmpty(val), column, val);
    }

    /**
     * likeRight
     */
    @Override
    public QueryWrapper<T> likeRight(String column, Object val) {
        return super.likeRight(FlymeUtils.isNotEmpty(val), column, val);
    }

    /**
     * 追加查询查询
     */
    public CriteriaQuery<T> addSelect(String... field) {

        for (String s : field) {
            this.select.add(s);
        }
        return this;
    }

    /**
     * 追加查询
     */
    public CriteriaQuery<T> select(Class cls, String... field) {
        TableAlias tableAlias = AnnotationUtils.getAnnotation(cls, TableAlias.class);
        String alias = tableAlias.value();
        for (String s : field) {
            this.select.add(alias + "." + s);
        }
        return this;
    }

    /**
     * 追加链接查询,默认left
     */
    public CriteriaQuery<T> joinSelect(Class cls, String... field) {
        createJoin(cls);
        TableAlias tableAlias = AnnotationUtils.getAnnotation(cls, TableAlias.class);
        String alias = tableAlias.value();
        for (String s : field) {
            this.select.add(alias + "." + s);
        }
        return this;
    }

    public PageParams getPageParams() {
        return pageParams;
    }

    public String getSelect() {
        StringBuffer str = new StringBuffer();
        String sqlSelect = getSqlSelect();
        if (FlymeUtils.isNotEmpty(sqlSelect)) {
            select.add(String.join(",", sqlSelect));
        }
        if (CollectionUtils.isEmpty(select)) {
            select.add("*");
        }
        return String.join(",", select);
    }

    public String getJoinSql() {
        StringBuilder builder = new StringBuilder();
        String sql = "%s JOIN %s %s ON %s.%s= %s.%s\n";
        if (FlymeUtils.isNotEmpty(joinSqList)) {
            joinSqList.forEach(item -> builder.append(String.format(sql, item.getJoinType().name(), item.getJoinTableName(), item.getJoinAlias(), item.getMainAlias(), item.getMainField(), item.getJoinAlias(), item.getJoinField())));
        }
        return builder.toString();
    }

    public EntityMap getRequestMap() {
        return requestMap;
    }

    public void setRequestMap(EntityMap requestMap) {
        this.requestMap = requestMap;
    }

    public void setRequestMap(PageParams pageParams) {
        this.requestMap.putAll(pageParams.getRequestMap());
    }

    public void setRequestMap(PageParams pageParams, EntityMap requestMap) {
        this.requestMap.putAll(pageParams.getRequestMap());
        this.requestMap.putAll(requestMap);
    }

    public <T> T getEntity(Class<T> t) {
        return FlymeUtils.getEntity(t, this.requestMap);
    }


    public String getHandlerName() {
        return handlerName;
    }

    public void setHandlerName(String handlerName) {
        this.handlerName = handlerName;
    }

    public PageModel getPageModel() {
        return pageModel;
    }

    public ListModel getListModel() {
        return listModel;
    }


    public T getModel() {
        return model;
    }

    public void setModel(T model) {
        this.model = model;
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public String getTableAlias() {
        return tableAlias;
    }

    public void setTableAlias(String tableAlias) {
        this.tableAlias = tableAlias;
    }

    public Class<T> getCls() {
        return cls;
    }

    public void setCls(Class<T> cls) {
        this.cls = cls;
    }

    /**
     * 清空查询字段
     */
    public void clear() {
        this.select.clear();
    }

    public String getIdField() {
        return idField;
    }

    public HashSet<String> getSetSelect() {
        return select;
    }
}

